package hu.debrecen.adastra.card.web;

import hu.debrecen.adastra.card.repository.domain.Account;
import hu.debrecen.adastra.card.service.AbstractGame;
import hu.debrecen.adastra.card.service.CardGameFactory;
import hu.debrecen.adastra.card.service.GameStoreService;
import hu.debrecen.adastra.card.service.SocialUserService;
import hu.debrecen.adastra.card.service.exceptions.FullGameException;
import hu.debrecen.adastra.card.service.impl.SnapszerCardGame;
import hu.debrecen.adastra.card.service.impl.ZsirozasCardGame;
import hu.debrecen.adastra.card.web.domain.AbstractGameStateVO;
import hu.debrecen.adastra.card.web.domain.AvailableGameVO;
import hu.debrecen.adastra.card.web.domain.GameListViewVO;
import hu.debrecen.adastra.card.web.domain.GameSummaryVO;
import hu.debrecen.adastra.card.web.domain.UserSocialReference;
import hu.debrecen.adastra.card.web.resolver.AutowiredHandlerAnnotationArgumentResolver.AutowiredHandler;

import javax.inject.Inject;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import com.google.common.base.Function;
import com.google.common.collect.Collections2;

@Controller
public class GameController extends AbstractController {

	private static final Logger LOGGER = LoggerFactory.getLogger(GameController.class);

	private final GameStoreService gameStoreService;

	private final CardGameFactory cardGameFactory;

	@Inject
	public GameController(final SocialUserService socialUserService, final GameStoreService gameService, final CardGameFactory cardGameFactory) {
		super(socialUserService);
		this.gameStoreService = gameService;
		this.cardGameFactory = cardGameFactory;
	}

	@RequestMapping(value = { "/games" }, method = RequestMethod.GET)
	@ResponseBody
	public GameListViewVO games(@AutowiredHandler final Account account, @RequestParam(value = "gameOwnerFilter", required = false) final String gameOwnerFilter) {

		Function<AbstractGame<?>, AvailableGameVO> function = new Function<AbstractGame<?>, AvailableGameVO>() {

			@Override
			public AvailableGameVO apply(final AbstractGame<?> game) {
				UserSocialReference userSocialReference = GameController.this.socialUserService.getUserSocialReference(game.getOwner());
				return new AvailableGameVO(game.getGameId(), userSocialReference);
			}

		};

		String gameId = null;
		try {
			gameId = this.gameStoreService.getGameByAccount(account).getGameId();
		} catch (Exception e) {
			// empty catch
		}

		return new GameListViewVO(gameId, Collections2.transform(this.gameStoreService.getAvailableGames(account, gameOwnerFilter), function));
	}

	@RequestMapping(value = { "/game" }, method = RequestMethod.PUT)
	@ResponseBody
	public String createGame(@AutowiredHandler final Account account, @RequestParam(value = "type", required = false) final String type) {
		GameController.LOGGER.debug("{} creates a new game.", account.getUsername());

		AbstractGame<?> cardGame = this.cardGameFactory.createCardGame(account, type);

		GameController.LOGGER.debug("{} type of game({}) is created.", cardGame.getClass().getName(), cardGame.getGameId());

		return this.gameStoreService.put(cardGame).getGameId();
	}

	@RequestMapping(value = { "/game/{gameId}" }, method = RequestMethod.POST)
	@ResponseBody
	public String join(@AutowiredHandler final Account account, @PathVariable("gameId") final String gameId) throws FullGameException {
		return this.gameStoreService.getGame(gameId).join(account).getGameId();
	}

	@RequestMapping(value = "/game/{gameId}", method = RequestMethod.GET)
	public String showGamePage(final Model model, @PathVariable("gameId") final String gameId) {

		if (!this.gameStoreService.exists(gameId)) {
			return "redirect:/";
		}

		model.addAttribute("gameId", gameId);

		AbstractGame<?> game = this.gameStoreService.getGame(gameId);

		if (game instanceof ZsirozasCardGame) {
			return "game/zsir2";
		}

		if (game instanceof SnapszerCardGame) {
			return "game/snapszer";
		}

		return "redirect:/";
	}

	@RequestMapping(value = "/game/{gameId}/state", method = RequestMethod.GET)
	@ResponseBody
	public AbstractGameStateVO getGame(@AutowiredHandler final Account account, @PathVariable("gameId") final String gameId) {
		return this.gameStoreService.getGame(gameId).getState(account);
	}

	@RequestMapping(value = "/game/{gameId}/call/{type}/{suit}", method = RequestMethod.POST)
	@ResponseBody
	public void call(@AutowiredHandler final Account account, @PathVariable final String gameId, @PathVariable final String type, @PathVariable final String suit) {
		this.gameStoreService.getGame(gameId).call(account, type, suit);
	}

	@RequestMapping(value = "/game/{gameId}/finish", method = RequestMethod.GET)
	@ResponseBody
	public GameSummaryVO finish(@AutowiredHandler final Account account, @PathVariable final String gameId) {
		return this.gameStoreService.getGame(gameId).finish(account);
	}

}
